#pragma once

struct Point3D;
struct Point2D;
struct Face;
class RenderObject;
class SpecialFX;
class Explosion;
class Water;
class Terrain;

#include <Windows.h>
#include <GL/GL.h>
#include <GL/GLU.h>
#include <vector>
#include <list>
#include <map>
#include <fstream>
#include "Defines.h"

#pragma pack(push, 1)

struct Colour
{
	Colour(GLfloat _R, GLfloat _G, GLfloat _B, GLfloat _A){R=_R; G=_G; B=_B; A=_A;};
	Colour(){R=1; G=1; B=1; A=1;};
	Colour(const Colour& copy){R = copy.R; G = copy.G; B = copy.B; A = copy.A;};
	GLfloat R;
	GLfloat G;
	GLfloat B;
	GLfloat A;
};
struct Point3D	//At project's it called as "BRect"
{
	Point3D(){X=0; Y=0; Z=0;};
	Point3D(float _X, float _Y, float _Z){X=_X; Y=_Y; Z=_Z;};
	Point3D& operator = (const Point3D &other){X=other.X; Y=other.Y; Z=other.Z;return *this;};
	float X, Y, Z;
};

struct Point2D 
{
	Point2D(){X=0; Y=0;};
	Point2D(float _X, float _Y){X=_X; Y=_Y;};
	Point2D& operator = (const Point2D &other){X=other.X; Y=other.Y;return *this;};
	float X, Y;
};

struct Face
{
	unsigned short int v1;
	unsigned short int v2;
	unsigned short int v3;
};
class RenderObject
{
public:
	RenderObject();
	RenderObject(const RenderObject&);
	virtual ~RenderObject(){;};

	bool Renderable;//
	Point3D	Pos;	//Position
	Point3D Rot;	//Rotation
	Point3D Color;	//X = r, Y = g, Z = b. Interval: [0..1]
	Point3D	Size;	//X - Width; Y - Height; Z - Depth

	virtual void Load(const char *szFileName);
	virtual void Unload();
	virtual void Render();
protected:
	std::vector<Point3D>	m_Vertexes;
	std::vector<Point3D>	m_Normals;
	std::vector<Point2D>	m_TexCoords;
	std::vector<Face>		m_Faces;
	unsigned char			m_Flags;	//Use texture? Use color? etc...

	//Texture function
	GLuint					m_uiTexture;
	//static std::map<std::string, GLuint>	m_GenTextures;
	//bool					m_LoadTexture;
	static bool				m_fnUnloadTexture(GLuint uiTexID){return true;};

	//3ds functions
	bool					m_Load3DS(const char *szFileName);
	unsigned int			m_Find3DSChunk(std::ifstream &ModelsFiles, unsigned short int uiChunk, bool fParent=true);
	void					m_fnPoint3DChangeYZ(Point3D &Point){float temp=Point.Y; Point.Y=Point.Z; Point.Z=temp;};

	//Flags
	static const unsigned char IsLoaded	= 1;
	static const unsigned char IsUseTexture	= 2;
};

class Camera
{
public:
	Camera();
	~Camera(){;};

	void Activate()const;
	Point3D	GetPosition()const;

	void SetAlpha(GLfloat Alpha);
	void SetBeta(GLfloat Beta);
	void SetDistance(GLfloat Distane);

	GLfloat	GetAlpha()const{return 180.0f*m_alpha/3.141592f;};
	GLfloat	GetBeta()const{return 180.0f*m_beta/3.141592f;};
	GLfloat	GetDistance()const{return m_distance;};

	Point3D	m_Focus;
private:
	GLfloat	m_alpha;	
	GLfloat	m_beta;
	GLfloat	m_distance;	//Distance from focus object to camera
	//GLfloat	PreA()const{return GetAlpha()>180?360-GetAlpha():GetAlpha();};		//Prepared alpha
	//GLfloat PreB()const{return GetAlpha()>180?180-GetBeta():GetBeta();};		//Prepared beta
	GLfloat	PreA()const{return m_alpha;};		//Prepared alpha
	GLfloat PreB()const{return m_beta;};		//Prepared beta
};

//struct SpecialEffectBehaviur
//class SpecialEffect
//{
//public:
	//SoecialEffect();
//};

struct Particle
{
	float x, y, z;
	float vx, vy, vz; //velocity
	float r, g, b, a;	//Color
	unsigned int uiLifeTime;
};

class SpecialFX: public RenderObject
{
public:
	SpecialFX(){m_PointSize=250.0f; m_nParticles = 2500; m_fPaused = false;};
	SpecialFX(const SpecialFX& copy);
	virtual ~SpecialFX(){;};								

	virtual void Load(const char *szFileName);
	void Update(unsigned int uiTime);
	virtual void Render();

	
protected:
	GLfloat				m_Intensity;		//Particles at millisecond
	GLuint				m_uiLifeTime;
	GLuint				m_nParticles;

	std::vector<Particle>	m_Particles;
	std::list<Particle>	m_States;
	std::vector<Colour> m_Colors;

	float m_PointSize;

	virtual Particle m_fnGenParticle();	
	virtual void m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle);//Only changes state, but not killing

	bool		m_fPaused; //Update willn't add new particles, but will update old particles
};

class Explosion: public SpecialFX
{
	friend class Manager;
public:
	Explosion();
	Explosion(const Explosion &copy);
	virtual ~Explosion();

	void Reset();
	bool IsCompleted();
	void Update(unsigned int uiTime );
protected:
	//virtual Particle	m_fnGenParticle();
	virtual Particle	m_fnGenParticle();
	virtual void		m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle);
	//
	//	m_uiLastTime+=uiTime; if(m_uiLastTime<m_uiActiveTime) SpecialFX::Update(uiTime);
	unsigned int		m_uiLastTime;
	static const unsigned int m_uiActiveTime = 1000000;
};

class Water: public SpecialFX
{
public:
	Water();
	virtual ~Water(){;};
protected:
	virtual Particle	m_fnGenParticle();
	virtual void		m_fnUpdateParticle(unsigned int uiTime, unsigned int uiParticle);
};

class Terrain : public RenderObject
{
public:
	Terrain(){;};
	~Terrain(){;};

	unsigned char Map[MAP_SIZE_Y][MAP_SIZE_X];

	virtual void Load(const char *szFileName);
	virtual void Render();
	void Update (unsigned int time);
private:
	std::vector<Water>	m_Waters;
};

#pragma pack(pop)